﻿/*************************************************************************
 *
 * Copyright (c) 2009-2015 Xuld. All rights reserved.
 * 
 * Project Url: http://work.xuld.net/circus
 * 
 * This source code is part of the Project Circus.
 * 
 * This code is licensed under The Circus License.
 * See the file License.html for the license details.
 * 
 * 
 * You must not remove this notice, or any other, from this software.
 *
 * 
 *************************************************************************/


using System;
using System.Drawing;

namespace Circus.Utility {

    /// <summary>
    /// 提供 D2D 的常用算法。
    /// </summary>
    public static class D2DHelper {

        #region 等比缩放

        /// <summary>
        /// 将指定的 <paramref name="width"/> 和 <paramref name="height"/> 等比例缩放，直到 <paramref name="width"/> 最大但不大于 <paramref name="maxWidth"/> 且 <paramref name="height"/> 最大但不大于 <paramref name="maxHeight"/> 。
        /// </summary>
        /// <param name="width">缩放前的宽度值。该值应为正数。</param>
        /// <param name="height">缩放前的高度值。该值应为正数。</param>
        /// <param name="maxWidth">缩放后的最大宽度值。该值应为正数。</param>
        /// <param name="maxHeight">缩放后的最大高度值。该值应为正数。</param>
        /// <returns>一个 <see cref="Size"/>，表示缩放后的大小，其 <c>Width</c> 等于 <paramref name="maxWidth"/> 或 <c>Height</c> 等于 <paramref name="maxHeight"/> ， 且 <c>Height</c> 与 <c>Width</c> 的比例和 <paramref name="width"/> 与 <paramref name="height"/> 的比例相同。</returns>
        public static Size scale(int width, int height, int maxWidth, int maxHeight) {
            if (width > maxWidth) {
                int wmh = width * maxHeight;
                int hmw = height * maxWidth;
                if (wmh > hmw) {
                    return new Size(maxWidth, hmw / width);
                } else
                    return new Size(wmh / height, maxHeight);
            } else {
                if (height > maxHeight)
                    return new Size(width * maxHeight / height, maxHeight);

                if (width == 0 || height == 0) {
                    return new Size(maxWidth, maxHeight);
                }

                int newHeight = maxWidth * height / width;

                if (newHeight > maxHeight)
                    return new Size(width * maxHeight / height, maxHeight);

                return new Size(maxWidth, newHeight);
            }
        }

        /// <summary>
        /// 将指定的 <paramref name="width"/> 和 <paramref name="height"/> 等比例缩放，直到 <paramref name="width"/> 最大但不大于 <paramref name="maxWidth"/> 且 <paramref name="height"/> 最大但不大于 <paramref name="maxHeight"/> 。
        /// </summary>
        /// <param name="width">缩放前的宽度值。该值应为正数。</param>
        /// <param name="height">缩放前的高度值。该值应为正数。</param>
        /// <param name="maxWidth">缩放后的最大宽度值。该值应为正数。</param>
        /// <param name="maxHeight">缩放后的最大高度值。该值应为正数。</param>
        /// <returns>一个 <see cref="SizeF"/>，表示缩放后的大小，其 <c>Width</c> 等于 <paramref name="maxWidth"/> 或 <c>Height</c> 等于 <paramref name="maxHeight"/> ， 且 <c>Height</c> 与 <c>Width</c> 的比例和 <paramref name="width"/> 与 <paramref name="height"/> 的比例相同。</returns>
        public static SizeF scale(float width, float height, float maxWidth, float maxHeight) {
            if (width > maxWidth) {
                float wmh = width * maxHeight;
                float hmw = height * maxWidth;
                if (wmh > hmw) {
                    return new SizeF(maxWidth, hmw / width);
                } else
                    return new SizeF(wmh / height, maxHeight);
            } else {
                if (height > maxHeight)
                    return new SizeF(width * maxHeight / height, maxHeight);
                return new SizeF(width, height);
            }
        }

        /// <summary>
        /// 将指定的 <paramref name="size"/> 的 <c>Width</c> 和 <c>Height</c> 等比例缩放，直到两者的值最大但分别不大于 <paramref name="maxSize"/> 的 <c>Width</c> 和 <c>Height</c>。
        /// </summary>
        /// <param name="size">缩放前的尺寸。</param>
        /// <param name="maxSize">缩放后的最大尺寸。</param>
        /// <returns>一个 <see cref="Size"/>，表示缩放后的大小，其 <c>Width</c> 等于 <paramref name="maxSize"/> 的 <c>Width</c> 或 <c>Height</c> 等于 <paramref name="maxSize"/> 的 <c>Height</c>， 且 <c>Height</c> 与 <c>Width</c> 的比例和 <paramref name="size"/> 的 <c>Width</c> 与 <paramref name="size"/> 的 <c>Height</c> 的比例相同。</returns>
        public static Size scale(this Size size, Size maxSize) {
            return scale(size.Width, size.Height, maxSize.Width, maxSize.Height);
        }

        /// <summary>
        /// 将指定的 <paramref name="size"/> 的 <c>Width</c> 和 <c>Height</c> 等比例缩放，直到两者的值最大但分别不大于 <paramref name="maxSize"/> 的 <c>Width</c> 和 <c>Height</c>。
        /// </summary>
        /// <param name="size">缩放前的尺寸。</param>
        /// <param name="maxSize">缩放后的最大尺寸。</param>
        /// <returns>一个 <see cref="SizeF"/>，表示缩放后的大小，其 <c>Width</c> 等于 <paramref name="maxSize"/> 的 <c>Width</c> 或 <c>Height</c> 等于 <paramref name="maxSize"/> 的 <c>Height</c>， 且 <c>Height</c> 与 <c>Width</c> 的比例和 <paramref name="size"/> 的 <c>Width</c> 与 <paramref name="size"/> 的 <c>Height</c> 的比例相同。</returns>
        public static SizeF scale(this SizeF size, SizeF maxSize) {
            return scale(size.Width, size.Height, maxSize.Width, maxSize.Height);
        }

        #endregion

        #region 位置

        /// <summary>
        /// 获取在指定区域内，由 <paramref name="alignment"/> 决定的真实位置。
        /// </summary>
        /// <param name="destRect">用于计算的目标区域。</param>
        /// <param name="alignment">计算使用的对齐方式。</param>
        /// <param name="srcWidth">放置的源的宽（单位为像素）。如果放置的是一个点，则值为零。</param>
        /// <param name="srcHeight">放置的源的高（单位为像素）。如果放置的是一个点，则值为零。</param>
        /// <param name="paddingX">目标区域的 x 方向内边距大小。</param>
        /// <param name="paddingY">目标区域的 y 方向内边距大小。</param>
        /// <returns>一个 <see cref="Point"/>，表示放置的源的左上角的位置。</returns>
        public static Point getPosition(Rectangle destRect, ContentAlignment alignment, int srcWidth = 0, int srcHeight = 0, int paddingX = 0, int paddingY = 0) {
            switch(alignment) {
                case ContentAlignment.BottomRight:
                    return new Point(destRect.Right - srcWidth - paddingX, destRect.Bottom - srcHeight - paddingY);
                case ContentAlignment.MiddleCenter:
                    return new Point(destRect.Left + ((destRect.Width + 1 - srcWidth) >> 1), destRect.Top + ((destRect.Height + 1 - srcHeight) >> 1));
                case ContentAlignment.TopLeft:
                    return new Point(destRect.Left + paddingX, destRect.Top + paddingY);
                case ContentAlignment.MiddleLeft:
                    return new Point(destRect.Left + paddingX, destRect.Top + ((destRect.Height + 1 - srcHeight) >> 1));
                case ContentAlignment.MiddleRight:
                    return new Point(destRect.Right - srcWidth - paddingX, destRect.Top + ((destRect.Height + 1 - srcHeight) >> 1));
                case ContentAlignment.TopCenter:
                    return new Point(destRect.Left + ((destRect.Width + 1 - srcWidth) >> 1), destRect.Top + paddingY);
                case ContentAlignment.TopRight:
                    return new Point(destRect.Right - srcWidth - paddingX, destRect.Top + paddingY);
                case ContentAlignment.BottomCenter:
                    return new Point(destRect.Left + ((destRect.Width + 1 - srcWidth) >> 1), destRect.Bottom - srcHeight - paddingY);
                case ContentAlignment.BottomLeft:
                    return new Point(destRect.Left + paddingX, destRect.Bottom - srcHeight - paddingY);
                default:
                    goto case ContentAlignment.BottomRight;
            }
        }

        /// <summary>
        /// 获取在指定区域内，由 <paramref name="alignment"/> 决定的真实位置。
        /// </summary>
        /// <param name="destRect">用于计算的目标区域。</param>
        /// <param name="alignment">计算使用的对齐方式。</param>
        /// <param name="srcWidth">放置的源的宽（单位为像素）。如果放置的是一个点，则值为零。</param>
        /// <param name="srcHeight">放置的源的高（单位为像素）。如果放置的是一个点，则值为零。</param>
        /// <param name="paddingX">目标区域的 x 方向内边距大小。</param>
        /// <param name="paddingY">目标区域的 y 方向内边距大小。</param>
        /// <returns>一个 <see cref="PointF"/>，表示放置的源的左上角的位置。</returns>
        public static PointF getPosition(RectangleF destRect, ContentAlignment alignment, float srcWidth = 0, float srcHeight = 0, float paddingX = 0, float paddingY = 0) {
            switch(alignment) {
                case ContentAlignment.BottomRight:
                    return new PointF(destRect.Right - srcWidth - paddingX, destRect.Bottom - srcHeight - paddingY);
                case ContentAlignment.MiddleCenter:
                    return new PointF(destRect.Left + ((destRect.Width + 1 - srcWidth) / 2), destRect.Top + ((destRect.Height + 1 - srcHeight) / 2));
                case ContentAlignment.TopLeft:
                    return new PointF(destRect.Left + paddingX, destRect.Top + paddingY);
                case ContentAlignment.MiddleLeft:
                    return new PointF(destRect.Left + paddingX, destRect.Top + ((destRect.Height + 1 - srcHeight) / 2));
                case ContentAlignment.MiddleRight:
                    return new PointF(destRect.Right - srcWidth - paddingX, destRect.Top + ((destRect.Height + 1 - srcHeight) / 2));
                case ContentAlignment.TopCenter:
                    return new PointF(destRect.Left + ((destRect.Width + 1 - srcWidth) / 2), destRect.Top + paddingY);
                case ContentAlignment.TopRight:
                    return new PointF(destRect.Right - srcWidth - paddingX, destRect.Top + paddingY);
                case ContentAlignment.BottomCenter:
                    return new PointF(destRect.Left + ((destRect.Width + 1 - srcWidth) / 2), destRect.Bottom - srcHeight - paddingY);
                case ContentAlignment.BottomLeft:
                    return new PointF(destRect.Left + paddingX, destRect.Bottom - srcHeight - paddingY);
                default:
                    goto case ContentAlignment.BottomRight;
            }
        }

        #endregion

    }

}
